/*******************************************************************************
 * Copyright (c) 2000, 2008 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.wst.jsdt.internal.ui.preferences;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipFile;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.ElementTreeSelectionDialog;
import org.eclipse.ui.dialogs.ISelectionStatusValidator;
import org.eclipse.ui.model.WorkbenchContentProvider;
import org.eclipse.ui.model.WorkbenchLabelProvider;
import org.eclipse.ui.views.navigator.ResourceComparator;
import org.eclipse.ui.wizards.datatransfer.ZipFileStructureProvider;
import org.eclipse.wst.jsdt.internal.corext.javadoc.JavaDocLocations;
import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin;
import org.eclipse.wst.jsdt.internal.ui.actions.OpenBrowserUtil;
import org.eclipse.wst.jsdt.internal.ui.dialogs.StatusInfo;
import org.eclipse.wst.jsdt.internal.ui.dialogs.StatusUtil;
import org.eclipse.wst.jsdt.internal.ui.util.PixelConverter;
import org.eclipse.wst.jsdt.internal.ui.wizards.IStatusChangeListener;
import org.eclipse.wst.jsdt.internal.ui.wizards.TypedElementSelectionValidator;
import org.eclipse.wst.jsdt.internal.ui.wizards.buildpaths.ArchiveFileFilter;
import org.eclipse.wst.jsdt.internal.ui.wizards.dialogfields.DialogField;
import org.eclipse.wst.jsdt.internal.ui.wizards.dialogfields.IDialogFieldListener;
import org.eclipse.wst.jsdt.internal.ui.wizards.dialogfields.LayoutUtil;
import org.eclipse.wst.jsdt.internal.ui.wizards.dialogfields.SelectionButtonDialogField;
import org.eclipse.wst.jsdt.internal.ui.wizards.dialogfields.StringDialogField;
import org.eclipse.wst.jsdt.ui.JavaScriptUI;

public class JavadocConfigurationBlock {
	private static final String FILE_IMPORT_MASK= "*.jar;*.zip"; //$NON-NLS-1$
	private static final String ERROR_DIALOG_TITLE= "Error Dialog"; //$NON-NLS-1$

	private StringDialogField fURLField;
	private StringDialogField fArchiveField;
	private StringDialogField fArchivePathField;
	private URL fInitialURL;
	private SelectionButtonDialogField fValidateURLButton;
	private SelectionButtonDialogField fValidateArchiveButton;
	private SelectionButtonDialogField fBrowseFolder;
	private SelectionButtonDialogField fURLRadioButton;
	private SelectionButtonDialogField fArchiveRadioButton;
	private SelectionButtonDialogField fBrowseArchive;
	private SelectionButtonDialogField fExternalRadio, fWorkspaceRadio;
	private SelectionButtonDialogField fBrowseArchivePath;
	private Shell fShell;
	private IStatusChangeListener fContext;
		
	private IStatus fURLStatus;
	private IStatus fArchiveStatus;
	private IStatus fArchivePathStatus;
	
	private URL fURLResult;
	private URL fArchiveURLResult;
	
	boolean fIsForSource;
	
	
	public JavadocConfigurationBlock(Shell shell,  IStatusChangeListener context, URL initURL, boolean forSource) {
		fShell= shell;
		fContext= context;
		fInitialURL= initURL;
		fIsForSource= forSource;
		
		JDocConfigurationAdapter adapter= new JDocConfigurationAdapter();
		
		if (!forSource) {
			fURLRadioButton= new SelectionButtonDialogField(SWT.RADIO);
			fURLRadioButton.setDialogFieldListener(adapter);
			fURLRadioButton.setLabelText(PreferencesMessages.JavadocConfigurationBlock_location_type_path_label); 
		}
		
		fURLField= new StringDialogField();
		fURLField.setDialogFieldListener(adapter);
		fURLField.setLabelText(PreferencesMessages.JavadocConfigurationBlock_location_path_label); 

		fBrowseFolder= new SelectionButtonDialogField(SWT.PUSH);
		fBrowseFolder.setDialogFieldListener(adapter);		
		fBrowseFolder.setLabelText(PreferencesMessages.JavadocConfigurationBlock_browse_folder_button); 

		fValidateURLButton= new SelectionButtonDialogField(SWT.PUSH);
		fValidateURLButton.setDialogFieldListener(adapter);		
		fValidateURLButton.setLabelText(PreferencesMessages.JavadocConfigurationBlock_validate_button); 

		if (!forSource) {
			fArchiveRadioButton= new SelectionButtonDialogField(SWT.RADIO);
			fArchiveRadioButton.setDialogFieldListener(adapter);
			fArchiveRadioButton.setLabelText(PreferencesMessages.JavadocConfigurationBlock_location_type_jar_label); 
	
			fExternalRadio= new SelectionButtonDialogField(SWT.RADIO);
			fExternalRadio.setDialogFieldListener(adapter);
			fExternalRadio.setLabelText(PreferencesMessages.JavadocConfigurationBlock_external_radio);
			
			fWorkspaceRadio= new SelectionButtonDialogField(SWT.RADIO);
			fWorkspaceRadio.setDialogFieldListener(adapter);
			fWorkspaceRadio.setLabelText(PreferencesMessages.JavadocConfigurationBlock_workspace_radio); 
			
			fArchiveField= new StringDialogField();
			fArchiveField.setDialogFieldListener(adapter);
			fArchiveField.setLabelText(PreferencesMessages.JavadocConfigurationBlock_location_jar_label); 
	
			fBrowseArchive= new SelectionButtonDialogField(SWT.PUSH);
			fBrowseArchive.setDialogFieldListener(adapter);		
			fBrowseArchive.setLabelText(PreferencesMessages.JavadocConfigurationBlock_browse_archive_button); 
			
			fArchivePathField= new StringDialogField();
			fArchivePathField.setDialogFieldListener(adapter);
			fArchivePathField.setLabelText(PreferencesMessages.JavadocConfigurationBlock_jar_path_label); 
			
			fBrowseArchivePath= new SelectionButtonDialogField(SWT.PUSH);
			fBrowseArchivePath.setDialogFieldListener(adapter);		
			fBrowseArchivePath.setLabelText(PreferencesMessages.JavadocConfigurationBlock_browse_archive_path_button); 
	
			fValidateArchiveButton= new SelectionButtonDialogField(SWT.PUSH);
			fValidateArchiveButton.setDialogFieldListener(adapter);		
			fValidateArchiveButton.setLabelText(PreferencesMessages.JavadocConfigurationBlock_validate_button); 
		}

		fURLStatus= new StatusInfo();
		fArchiveStatus= new StatusInfo();
		fArchivePathStatus= new StatusInfo();
		
		initializeSelections();
	}
	
	public Control createContents(Composite parent) {
		fShell= parent.getShell();
		
		PixelConverter converter= new PixelConverter(parent);
		Composite topComp= new Composite(parent, SWT.NONE);
		GridLayout topLayout= new GridLayout();
		topLayout.numColumns= 3;
		topLayout.marginWidth= 0;
		topLayout.marginHeight= 0;
		topComp.setLayout(topLayout);

		// Add the first radio button for the path
		if (!fIsForSource) {
			fURLRadioButton.doFillIntoGrid(topComp, 3);
		}
	
		fURLField.doFillIntoGrid(topComp, 2);
		LayoutUtil.setWidthHint(fURLField.getTextControl(null), converter.convertWidthInCharsToPixels(43));
		LayoutUtil.setHorizontalGrabbing(fURLField.getTextControl(null));		

		fBrowseFolder.doFillIntoGrid(topComp, 1);
		
		DialogField.createEmptySpace(topComp, 2);			
		fValidateURLButton.doFillIntoGrid(topComp, 1);

		//DialogField.createEmptySpace(topComp, 3);	
		
		if (!fIsForSource) {
			// Add the second radio button for the jar/zip
			fArchiveRadioButton.doFillIntoGrid(topComp, 3);
	
			
			// external - workspace selection
			DialogField.createEmptySpace(topComp, 1);
			Composite radioComposite= new Composite(topComp, SWT.NONE);
			radioComposite.setLayoutData(new GridData(GridData.FILL, GridData.FILL, false, false));
			GridLayout layout= new GridLayout(2, true);
			layout.marginHeight= 0;
			layout.marginWidth= 0;
			radioComposite.setLayout(layout);
			fExternalRadio.doFillIntoGrid(radioComposite, 1);
			fWorkspaceRadio.doFillIntoGrid(radioComposite, 1);
			DialogField.createEmptySpace(topComp, 1);
			
			// Add the jar/zip field
			fArchiveField.doFillIntoGrid(topComp, 2);
			LayoutUtil.setWidthHint(fArchiveField.getTextControl(null), converter.convertWidthInCharsToPixels(43));
			LayoutUtil.setHorizontalGrabbing(fArchiveField.getTextControl(null));		

			fBrowseArchive.doFillIntoGrid(topComp, 1);

			// Add the path chooser for the jar/zip
			fArchivePathField.doFillIntoGrid(topComp, 2);
			LayoutUtil.setWidthHint(fArchivePathField.getTextControl(null), converter.convertWidthInCharsToPixels(43));
			LayoutUtil.setHorizontalGrabbing(fArchivePathField.getTextControl(null));	
			
			fBrowseArchivePath.doFillIntoGrid(topComp, 1);
			
			DialogField.createEmptySpace(topComp, 2);
			fValidateArchiveButton.doFillIntoGrid(topComp, 1);

			int indent= converter.convertWidthInCharsToPixels(2);	
			LayoutUtil.setHorizontalIndent(fArchiveField.getLabelControl(null), indent);
			LayoutUtil.setHorizontalIndent(fArchivePathField.getLabelControl(null), indent);
			LayoutUtil.setHorizontalIndent(fURLField.getLabelControl(null), indent);
			
			fURLRadioButton.attachDialogFields(new DialogField[] {fURLField,  fBrowseFolder, fValidateURLButton });
			fArchiveRadioButton.attachDialogFields(new DialogField[] {fArchiveField,  fBrowseArchive, fExternalRadio, fWorkspaceRadio, fArchivePathField, fBrowseArchivePath, fValidateArchiveButton });
		}

		
		return topComp;
	}	
	
	private void initializeSelections() {
		String initialValue = fInitialURL != null ? fInitialURL.toExternalForm() : ""; //$NON-NLS-1$
		
		if (fIsForSource) {
			fURLField.setText(initialValue);
			return;
		}
		String prefix= JavaDocLocations.ARCHIVE_PREFIX;
		boolean isArchive= initialValue.startsWith(prefix);
		
		boolean isWorkspaceArchive= false;
		
		fURLRadioButton.setSelection(!isArchive);
		fArchiveRadioButton.setSelection(isArchive);
		
		if (isArchive) {
			String jarPathStr;
			String insidePath= ""; //$NON-NLS-1$
			int excIndex= initialValue.indexOf("!/"); //$NON-NLS-1$
			if (excIndex == -1) {
				jarPathStr= initialValue.substring(prefix.length());
			} else {
				jarPathStr= initialValue.substring(prefix.length(), excIndex);
				insidePath= initialValue.substring(excIndex + 2);
			}
			
			final String fileProtocol= "file:/"; //$NON-NLS-1$
			final String resourceProtocol= "platform:/resource/"; //$NON-NLS-1$
			
			if (jarPathStr.startsWith(fileProtocol)) {
				jarPathStr= jarPathStr.substring(fileProtocol.length());
			} else if (jarPathStr.startsWith(resourceProtocol)) {
				jarPathStr= jarPathStr.substring(resourceProtocol.length());
				isWorkspaceArchive= true;
			} else {
				fURLField.setText(initialValue);
				return;
			}
			IPath jarPath= new Path(decodeExclamationMarks(jarPathStr));
			fArchivePathField.setText(decodeExclamationMarks(insidePath));
			if (isWorkspaceArchive) {
				fArchiveField.setText(jarPath.makeRelative().toString());
			} else {
				fArchiveField.setText(jarPath.makeAbsolute().toOSString());
			}
		} else {
			fURLField.setText(initialValue);
		}
		fExternalRadio.setSelection(!isWorkspaceArchive);
		fWorkspaceRadio.setSelection(isWorkspaceArchive);
		
	}
		
	public void setFocus() {
		fURLField.postSetFocusOnDialogField(fShell.getDisplay());
	}
	
	public void performDefaults() {
		initializeSelections();
	}
	
	public URL getJavadocLocation() {
		if (fIsForSource || fURLRadioButton.isSelected()) {
			return fURLResult;
		}
		return fArchiveURLResult;
	}
		
	private class EntryValidator implements Runnable {

		private String fInvalidMessage= PreferencesMessages.JavadocConfigurationBlock_InvalidLocation_message; 
		private String fValidMessage= PreferencesMessages.JavadocConfigurationBlock_ValidLocation_message; 
		private String fTitle=  PreferencesMessages.JavadocConfigurationBlock_MessageDialog_title; 
		private String fUnable= PreferencesMessages.JavadocConfigurationBlock_UnableToValidateLocation_message; 
		public void run() {

			URL location= getJavadocLocation();
			if (location == null) {
				MessageDialog.openWarning(fShell, fTitle, fInvalidMessage); 
				return;
			}

			try {
				String protocol = location.getProtocol();
				if (protocol.startsWith("http") || protocol.equals("jar")) { //$NON-NLS-1$ //$NON-NLS-2$
					validateURL(location);
				} else if (protocol.equals("file")) { //$NON-NLS-1$
					validateFile(location);
				} else {
					MessageDialog.openWarning(fShell, fTitle, fUnable); 
				}
			} catch (MalformedURLException e) {
				MessageDialog.openWarning(fShell, fTitle, fUnable); 
			}

		}
		
		public void spawnInBrowser(URL url) {
			OpenBrowserUtil.open(url, fShell.getDisplay(), fTitle);
		}

		private void validateFile(URL location) throws MalformedURLException {
			File folder = new File(location.getFile());
			if (folder.isDirectory()) {
				File indexFile= new File(folder, "index.html"); //$NON-NLS-1$
				if (indexFile.isFile()) {
					if (MessageDialog.openConfirm(fShell, fTitle, fValidMessage)) { 
						spawnInBrowser(indexFile.toURL());
					}
					return;
				}
			}
			MessageDialog.openWarning(fShell, fTitle, fInvalidMessage); 
		}
		
		private void validateURL(URL location) throws MalformedURLException {
			IPath path= new Path(location.toExternalForm());
			IPath index = path.append("index.html"); //$NON-NLS-1$
			IPath packagelist = path.append("package-list"); //$NON-NLS-1$
			URL indexURL = new URL(index.toString());
			URL packagelistURL = new URL(packagelist.toString());

			boolean suc= checkURLConnection(indexURL) && checkURLConnection(packagelistURL);
			if (suc) {
				if (MessageDialog.openConfirm(fShell, fTitle, fValidMessage)) {
					spawnInBrowser(indexURL);
				}
			} else { 
				MessageDialog.openWarning(fShell, fTitle, fInvalidMessage);
			}
		}
	}
	
	private boolean checkURLConnection(URL url) {
		int res= 0;
		URLConnection connection= null;
		try {
			connection= url.openConnection();
			if (connection instanceof HttpURLConnection) {
				connection.connect();
				res= ((HttpURLConnection) connection).getResponseCode();
			}
			InputStream is= null;
			try {
				is= connection.getInputStream();
				byte[] buffer= new byte[256];
				while (is.read(buffer) != -1) {
				}
			} finally {
				if (is != null) {
					is.close();
				}
			}
		} catch (IllegalArgumentException e) {
			return false; // bug 91072
		} catch (IOException e) {
			return false;
		}
		return res < 400; 
	}
 	
	
	private class JDocConfigurationAdapter implements IDialogFieldListener {

		// ---------- IDialogFieldListener --------
		public void dialogFieldChanged(DialogField field) {
			jdocDialogFieldChanged(field);
		}
	}


	private void jdocDialogFieldChanged(DialogField field) {
		if (field == fURLField) {
			fURLStatus= updateURLStatus();
			statusChanged();
		} else if (field == fArchiveField) {
			fArchiveStatus= updateArchiveStatus();
			statusChanged();
		} else if (field == fArchivePathField) {
			fArchivePathStatus= updateArchivePathStatus();
			statusChanged();
		} else if ((field == fValidateURLButton) || (field == fValidateArchiveButton)) {
			EntryValidator validator= new EntryValidator();
			BusyIndicator.showWhile(fShell.getDisplay(), validator);
		} else if (field == fBrowseFolder) {
			String url= chooseJavaDocFolder();
			if (url != null) {
				fURLField.setText(url);
			}
		} else if (field == fBrowseArchive) {
			String jarPath= chooseArchive();
			if (jarPath != null) {
				fArchiveField.setText(jarPath);
			}
		} else if ((field == fExternalRadio) || (field == fWorkspaceRadio)) {
			fArchiveStatus= updateArchiveStatus();
			statusChanged();	
		} else if (field == fBrowseArchivePath) {
			String archivePath= chooseArchivePath();
			if (archivePath != null) {
				fArchivePathField.setText(archivePath);
			}		
		} else if ((field == fURLRadioButton) || (field == fArchiveRadioButton)) {
			statusChanged();							
		}
	}
	
	private void statusChanged() {
		IStatus status;
		boolean isURL= fIsForSource || fURLRadioButton.isSelected();
		if (isURL) {
			status= fURLStatus;
		} else {
			status= StatusUtil.getMoreSevere(fArchiveStatus, fArchivePathStatus);
		}
		if (!fIsForSource) {
			boolean canBrowseArchivePath= !isURL && fArchiveStatus.isOK() && (fArchiveField.getText().length() > 0);
			if (canBrowseArchivePath && fWorkspaceRadio.isSelected()) {
				IResource resource= ResourcesPlugin.getWorkspace().getRoot().findMember(new Path(fArchiveField.getText()));
				canBrowseArchivePath= (resource != null) && (resource.getLocation() != null);
			}
			fBrowseArchivePath.setEnabled(canBrowseArchivePath);
		}
		fContext.statusChanged(status);
	}


	private String chooseArchivePath() {
		final String[] res= new String[] { null };
		BusyIndicator.showWhile(fShell.getDisplay(), new Runnable() {
			public void run() {
				res[0]= internalChooseArchivePath();
			}
		});
		return res[0];
	}
	
	private String encodeExclamationMarks(String str) {
		StringBuffer buf= new StringBuffer(str.length());
		for (int i= 0; i < str.length(); i++) {
			char ch= str.charAt(i);
			if (ch == '!') {
				buf.append("%21"); //$NON-NLS-1$
			} else {
				buf.append(ch);
			}
		}
		return buf.toString();
	}
	
	private String decodeExclamationMarks(String str) {
		StringBuffer buf= new StringBuffer(str.length());
		int length= str.length();
		for (int i= 0; i < length; i++) {
			char ch= str.charAt(i);
			if ((ch == '%') && (i < (length - 2)) && (str.charAt(i + 1) == '2') && (str.charAt(i + 2) == '1')) {
				buf.append('!');
				i+= 2;
			} else {
				buf.append(ch);
			}
		}
		return buf.toString();
	}
	
		

	private String internalChooseArchivePath() {		
		ZipFile zipFile= null;
		try {
			if (fWorkspaceRadio.isSelected()) {
				IResource resource= ResourcesPlugin.getWorkspace().getRoot().findMember(new Path(fArchiveField.getText()));
				if (resource != null) {
					IPath location= resource.getLocation();
					if (location != null) {
						zipFile= new ZipFile(location.toOSString());
					}
				}
			} else {
				zipFile= new ZipFile(fArchiveField.getText());
			}
			if (zipFile == null) {
				return null;
			}
			
			ZipFileStructureProvider provider= new ZipFileStructureProvider(zipFile);
			
			ILabelProvider lp= new ZipDialogLabelProvider(provider);
			ZipDialogContentProvider cp= new ZipDialogContentProvider(provider);
						
			ElementTreeSelectionDialog dialog= new ElementTreeSelectionDialog(fShell, lp, cp);
			dialog.setAllowMultiple(false);
			dialog.setValidator(new ZipDialogValidator());
			dialog.setTitle(PreferencesMessages.JavadocConfigurationBlock_browse_jarorzip_path_title); 
			dialog.setMessage(PreferencesMessages.JavadocConfigurationBlock_location_in_jarorzip_message); 
			dialog.setComparator(new ViewerComparator());
			
			String init= fArchivePathField.getText();
			if (init.length() == 0) {
				init= "docs/api"; //$NON-NLS-1$
			}
			dialog.setInitialSelection(cp.findElement(new Path(init)));
			
			dialog.setInput(this);
			if (dialog.open() == Window.OK) {
				String name= provider.getFullPath(dialog.getFirstResult());
				return new Path(name).removeTrailingSeparator().toString();
			}
		} catch (IOException e) {
			JavaScriptPlugin.log(e);
		} finally {
			if (zipFile != null) {
				try {
					zipFile.close();
				} catch (IOException e1) {
					// ignore
				}
			}
		}
		return null;
	}

	private String chooseArchive() {
		if (fWorkspaceRadio.isSelected()) {
			return chooseWorkspaceArchive();
		}
		
		IPath currPath= new Path(fArchiveField.getText());
		if (ArchiveFileFilter.isArchivePath(currPath)) {
			currPath= currPath.removeLastSegments(1);
		}
		
		FileDialog dialog= new FileDialog(fShell, SWT.OPEN);
		dialog.setFilterExtensions(new String[] { FILE_IMPORT_MASK });
		dialog.setText(PreferencesMessages.JavadocConfigurationBlock_zipImportSource_title);
		dialog.setFilterPath(currPath.toOSString());

		return dialog.open();
	}
	
	private String chooseWorkspaceArchive() {
		String initSelection= fArchiveField.getText();
		
		ILabelProvider lp= new WorkbenchLabelProvider();
		ITreeContentProvider cp= new WorkbenchContentProvider();
		Class[] acceptedClasses= new Class[] { IFile.class };
		TypedElementSelectionValidator validator= new TypedElementSelectionValidator(acceptedClasses, true);

		IResource initSel= null;
		IWorkspaceRoot root= ResourcesPlugin.getWorkspace().getRoot();
		if (initSelection.length() > 0) {
			initSel= root.findMember(new Path(initSelection));
		}

		ElementTreeSelectionDialog dialog= new ElementTreeSelectionDialog(fShell, lp, cp);
		dialog.addFilter(new ArchiveFileFilter((List) null, true));
		dialog.setAllowMultiple(false);
		dialog.setValidator(validator);
		dialog.setComparator(new ResourceComparator(ResourceComparator.NAME));
		dialog.setTitle(PreferencesMessages.JavadocConfigurationBlock_workspace_archive_selection_dialog_title); 
		dialog.setMessage(PreferencesMessages.JavadocConfigurationBlock_workspace_archive_selection_dialog_description); 
		dialog.setInput(root);
		dialog.setInitialSelection(initSel);
		dialog.setHelpAvailable(false);
		if (dialog.open() == Window.OK) {
			IResource res= (IResource) dialog.getFirstResult();
			return res.getFullPath().makeRelative().toString();
		}
		return null;
	}
	
	/**
	 * Display an error dialog with the specified message.
	 *
	 * @param message the error message
	 */
	protected void displayErrorDialog(String message) {
		MessageDialog.openError(fShell, ERROR_DIALOG_TITLE, message); 
	}
		
	private String chooseJavaDocFolder() {
		String initPath= ""; //$NON-NLS-1$
		if ((fURLResult != null) && "file".equals(fURLResult.getProtocol())) { //$NON-NLS-1$
			initPath= (new File(fURLResult.getFile())).getPath();
		}
		DirectoryDialog dialog= new DirectoryDialog(fShell);
		dialog.setText(PreferencesMessages.JavadocConfigurationBlock_javadocFolderDialog_label); 
		dialog.setMessage(PreferencesMessages.JavadocConfigurationBlock_javadocFolderDialog_message); 
		dialog.setFilterPath(initPath);
		String result= dialog.open();
		if (result != null) {
			try {
				URL url= new File(result).toURL();
				return url.toExternalForm();
			} catch (MalformedURLException e) {
				JavaScriptPlugin.log(e);
			}
		}
		return null;
	}
		
	private IStatus updateURLStatus() {
		StatusInfo status= new StatusInfo();
		fURLResult= null;
		try {
			String jdocLocation= fURLField.getText();
			if (jdocLocation.length() == 0) {
				return status;
			}
			URL url= new URL(jdocLocation);
			if ("file".equals(url.getProtocol())) { //$NON-NLS-1$
				if (url.getFile() == null) {
					status.setError(PreferencesMessages.JavadocConfigurationBlock_error_notafolder); 
					return status;
				}
			}
			fURLResult= url;
		} catch (MalformedURLException e) {
			status.setError(PreferencesMessages.JavadocConfigurationBlock_MalformedURL_error);  
			return status;			
		}

		return status;
	}	
	
	private IStatus updateArchiveStatus() {
		try {
			fArchiveURLResult= null;
			
			StatusInfo status= new StatusInfo();
			String jdocLocation= fArchiveField.getText();
			if (jdocLocation.length() > 0)  {
				if (!Path.ROOT.isValidPath(jdocLocation)) {
					status.setError(PreferencesMessages.JavadocConfigurationBlock_error_invalidarchivepath); 
					return status;	
				}
				if (fWorkspaceRadio.isSelected()) {
					IWorkspaceRoot root= ResourcesPlugin.getWorkspace().getRoot();
					IResource res= root.findMember(new Path(jdocLocation));
					if (res != null) {
						if (!(res instanceof IFile)) {
							status.setError(PreferencesMessages.JavadocConfigurationBlock_error_archive_not_found_in_workspace); 
							return status;
						}
					} else {
						status.setError(PreferencesMessages.JavadocConfigurationBlock_error_archive_not_found_in_workspace); 
						return status;	
					}
				} else {
					IPath path= Path.fromOSString(jdocLocation);
					if (!path.isAbsolute()) {
						status.setError(PreferencesMessages.JavadocConfigurationBlock_error_archivepathnotabsolute); 
						return status;	
					}
					File jarFile= new File(jdocLocation);
					if (jarFile.isDirectory())  {
						status.setError(PreferencesMessages.JavadocConfigurationBlock_error_notafile); 
						return status;							
					}
					if (!jarFile.exists())  {
						status.setWarning(PreferencesMessages.JavadocConfigurationBlock_error_notafile); 
					}
				}
				fArchiveURLResult= getArchiveURL();
			}
			return status;
		} catch (MalformedURLException e) {
			StatusInfo status= new StatusInfo();
			status.setError(e.getMessage());  
			return status;
		}
	}
	
	private IStatus updateArchivePathStatus() {
		// no validation yet
		try {
			fArchiveURLResult= getArchiveURL();
		} catch (MalformedURLException e) {
			fArchiveURLResult= null;
			StatusInfo status= new StatusInfo();
			status.setError(e.getMessage());  
			//status.setError(PreferencesMessages.getString("JavadocConfigurationBlock.MalformedURL.error"));  //$NON-NLS-1$
			return status;
		}
		return new StatusInfo();
	
	}
	
	
	private URL getArchiveURL() throws MalformedURLException {
		String jarLoc= fArchiveField.getText();
		String innerPath= fArchivePathField.getText().trim();
		
		StringBuffer buf= new StringBuffer();
		buf.append("jar:"); //$NON-NLS-1$
		
		if (fWorkspaceRadio.isSelected()) {
			IWorkspaceRoot root= ResourcesPlugin.getWorkspace().getRoot();
			IResource res= root.findMember(new Path(jarLoc));
			if (res != null) {
				buf.append("platform:/resource").append(encodeExclamationMarks(res.getFullPath().toString())); //$NON-NLS-1$
			}
		} else {
			buf.append(encodeExclamationMarks(new File(jarLoc).toURL().toExternalForm()));
		}
		buf.append('!');
		if (innerPath.length() > 0) {
			if (innerPath.charAt(0) != '/') {
				buf.append('/');
			}
			buf.append(innerPath);
		} else {
			buf.append('/');
		}
		return new URL(buf.toString());
	}
	

	/**
	 * An adapter for presenting a zip file in a tree viewer.
	 */
	private static class ZipDialogContentProvider implements ITreeContentProvider {
	
		private ZipFileStructureProvider fProvider;
		
		public ZipDialogContentProvider(ZipFileStructureProvider provider) {
			fProvider= provider;
		}

		public Object findElement(IPath path) {
			String[] segments= path.segments();
			
			Object elem= fProvider.getRoot();
			for (int i= 0; (i < segments.length) && (elem != null); i++) {
				List list= fProvider.getChildren(elem);
				String name= segments[i];
				elem= null;
				for (int k= 0; k < list.size(); k++) {
					Object curr= list.get(k);
					if (fProvider.isFolder(curr) && name.equals(fProvider.getLabel(curr))) {
						elem= curr;
						break;
					}
				}
			}
			return elem;
		}
		
		private Object recursiveFind(Object element, String name) {
			if (name.equals(fProvider.getLabel(element))) {
				return element;
			}
			List list= fProvider.getChildren(element);
			if (list != null) {
				for (int k= 0; k < list.size(); k++) {
					Object res= recursiveFind(list.get(k), name);
					if (res != null) {
						return res;
					}
				}				
			}
			return null;
		}
		
		/* non java-doc
		 * @see ITreeContentProvider#inputChanged
		 */
		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
		}
	
		/* non java-doc
		  * @see ITreeContentProvider#getParent
		  */
		public Object getParent(Object element) {
			if (element.equals(fProvider.getRoot())) {
				return null;
			}
			IPath path= new Path(fProvider.getFullPath(element));
			if (path.segmentCount() > 0) {
				return findElement(path.removeLastSegments(1));
			}
			return fProvider.getRoot();
		}
	
		/* non java-doc
		 * @see ITreeContentProvider#hasChildren
		 */
		public boolean hasChildren(Object element) {
			List list= fProvider.getChildren(element);
			if (list != null) {
				for (int i= 0; i < list.size(); i++) {
					if (fProvider.isFolder(list.get(i))) {
						return true;
					}
				}
			}
			return false;
		}
	
		/* non java-doc
		 * @see ITreeContentProvider#getChildren
		 */
		public Object[] getChildren(Object element) {
			List list= fProvider.getChildren(element);
			ArrayList res= new ArrayList();
			if (list != null) {
				for (int i= 0; i < list.size(); i++) {
					Object curr= list.get(i);
					if (fProvider.isFolder(curr)) {
						res.add(curr);
					}
				}
			}
			return res.toArray();
		}
	
		/* non java-doc
		 * @see ITreeContentProvider#getElements
		 */
		public Object[] getElements(Object element) {
			return new Object[] {fProvider.getRoot() };
		}
	
		/* non java-doc
		 * @see IContentProvider#dispose
		 */
		public void dispose() {
		}
	}
		
	private static class ZipDialogLabelProvider extends LabelProvider {
	
		private final Image IMG_JAR=
			JavaScriptUI.getSharedImages().getImage(org.eclipse.wst.jsdt.ui.ISharedImages.IMG_OBJS_JAR);
		private final Image IMG_FOLDER=
			PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FOLDER);
	
		private ZipFileStructureProvider fProvider;
	
		public ZipDialogLabelProvider(ZipFileStructureProvider provider) {
			fProvider= provider;
		}
	
		public Image getImage(Object element) {
			if (element == fProvider.getRoot()) {
				return IMG_JAR;
			} else {
				return IMG_FOLDER;
			}
		}
	
		public String getText(Object element) {
			if (element == fProvider.getRoot()) {
				return fProvider.getZipFile().getName();
			}
			return fProvider.getLabel(element);
		}
	}
	
	private static class ZipDialogValidator implements ISelectionStatusValidator {
		public ZipDialogValidator() {
			super();
		}		
			
		/*
		 * @see ISelectionValidator#validate(Object[])
		 */
		public IStatus validate(Object[] selection) {
			String message= ""; //$NON-NLS-1$
			return new StatusInfo(IStatus.INFO, message);
		}
	}	

}
